<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        * {
            padding: 0;
            margin: 0;
        }

        .button-group {
            margin-top: 20px;
        }

        li {
            list-style: none;
        }

        .list li {
            height: 40px;
            line-height: 40px;
            border: 1px dashed #333;
        }

        .list li+li {
            border-top: 0;
        }

        .active {
            background-color: #6ff;
        }
    </style>
</head>

<body>
    <div class="wrap">
        <h2 id="title">hello</h2>
        <ul class="list">
            <li id="first">01</li>
            <li>02</li>
            <li>03</li>
            <li>04</li>
            <li>05</li>
            <li>06</li>
        </ul>
    </div>
    <div class="button-group">
        <button class="setHTML">设置html</button>
        <button class="getHTML">获取html</button>
        <button class="hide">hide</button>
        <button class="show">show</button>
        <button class="setCss">设置样式</button>
        <button class="getCss">获取样式</button>
        <button class="setWidth">设置宽度</button>
        <button class="getWidth">设置宽度</button>
        <button class="setAttr">设置HTML属性</button>
        <button class="getAttr">获取HTML属性</button>
        <button class="setProp">prop设置</button>
        <button class="getProp">prop获取</button>
        <button class="addClass">addClass</button>
        <button class="removeClass">removeClass</button>
        <button class="hasClass">hasClass</button>
        <button class="getEles">获取元素补充</button>
        <button class="eq">eq</button>
        <button class="siblings">siblings</button>
        <button class="chain">链式操作</button>
    </div>
</body>
<script>

    // 优化版(伪数组)  => 获取元素返回伪数组

    // 当前GetEle只能通过css选择器 获取元素 返回伪数组  => 太单一

    // selector
    //     字符串  => css选择器
    //     传入单个原生dom元素  => 单元素先放到数组中,在改为伪数组  [ele]  [].__propto__ = GetEle.prototype
    //     传入多个原生dom元素  => 一般都会放到集合中传入  [li,li,li,li,li]   先转真数组,在改为伪数组


    // GetEle(first)     传入单个原生dom元素
    // GetEle([li,li,li,li,li])    传入多个原生dom元素

    function GetEle(selector) {
        // {}
        // {}[[Prototype]] = GetEle.prototype;
        // this -> {}

        var eles = [];
        if (typeof selector == "string") {  // css选择器
            eles = document.querySelectorAll(selector);  // 获取元素(伪数组) => 用于后期操作元素
        } else if (selector instanceof Element) {  // 单个原生dom元素
            eles.push(selector);
        } else if (typeof selector == "object" && selector.length >= 0) {  // 传入多个原生dom元素
            eles = selector;
        }

        eles = Array.from(eles); // 伪数组转真数组
        Object.setPrototypeOf(eles, GetEle.prototype);

        return eles;  // 自定义实例化对象(伪数组)  this => 伪数组 (有数组的特性但是原型属性[[Prototype]] => GetEle.Prototype)
    }

    // ------------------遍历伪数组 操作伪数组中的每一个元素 => 取值/赋值---------------------------

    // text()   获取和设置文本内容(操作实例化(伪数组中的所有元素))  
    // text("123123")  有参数就设置
    // text()          没有参数就获取   
    GetEle.prototype.text = function (con) {
        // this -> 调用此方法的实例化对象(伪数组 -> 元素的集合)
        if (arguments.length >= 1) { // 有参数 => 设置
            // for (var i = 0; i < this.length; i++) {
            //     var ele = this[i];
            //     ele.innerText = con;
            // }

            this.each(function (ele, i) {
                ele.innerText = con;
            })

            return this;  // 返回原本的
            // return $(this); // this->实例化对象 (伪数组)  => $()可以接收伪数组 => 返回新的实例化化对象

        } else { // 没有参数
            return this[0].innerText;
        }
    }

    // html()   获取和设置html结构(操作实例化(伪数组中的所有元素))  
    // html("123123")  有参数就设置
    // html()          没有参数就获取 
    GetEle.prototype.html = function (con) {
        // this -> 调用此方法的实例化对象(伪数组 -> 元素的集合)
        if (arguments.length >= 1) { // 有参数 => 设置
            // for (var i = 0; i < this.length; i++) {
            //     var ele = this[i];
            //     ele.innerHTML = con;
            // }
            this.each(function (ele, i) {
                ele.innerHTML = con;
            })
            return this;  // 返回原本的
        } else { // 没有参数
            return this[0].innerHTML;
        }
    }

    // val()   获取和设置元素的value值(操作实例化对象(伪数组中的所有元素))  
    // val("123123")  有参数就设置
    // val()          没有参数就获取 
    GetEle.prototype.val = function (con) {
        // this -> 调用此方法的实例化对象(伪数组 -> 元素的集合)
        if (arguments.length >= 1) { // 有参数 => 设置
            // for (var i = 0; i < this.length; i++) {
            //     var ele = this[i];
            //     ele.value = con;
            // }

            this.each(function (ele, i) {
                ele.value = con;
            })
            return this;  // 返回原本的
        } else { // 没有参数
            return this[0].value;
        }
    }

    // hide()  元素隐藏(操作实例化对象(伪数组中的所有元素) => 将去隐藏)
    // show()  元素显示(操作实例化对象(伪数组中的所有元素) => 将去隐藏)
    GetEle.prototype.hide = function () {
        // this -> 调用此方法的实例化对象(伪数组 -> 元素的集合)
        // console.log("this",this);
        // for(var i=0;i<this.length;i++){
        //     var ele = this[i];
        //     ele.style.display = "none";
        // }

        this.each(function (ele, i) {
            ele.style.display = "none";
        })
        return this;  // 返回原本的
    }

    GetEle.prototype.show = function () {
        // this -> 调用此方法的实例化对象(伪数组 -> 元素的集合)
        // console.log("this",this);
        // for(var i=0;i<this.length;i++){
        //     var ele = this[i];
        //     ele.style.display = "block";
        // }
        this.each(function (ele, i) {
            ele.style.display = "block";
        })
        return this;  // 返回原本的
    }

    // on(type,callback)  绑定事件
    // type => 绑定的事件类型
    // callback => 事件触发时执行的函数 (事件函数中的this指向绑定事件的元素)
    GetEle.prototype.on = function (type, callback) {
        // this -> 调用此方法的实例化对象(伪数组 -> 元素的集合)
        // for(var i=0;i<this.length;i++){
        //     var ele = this[i];
        //     ele.addEventListener(type,callback);
        // }

        this.each(function (ele, i) {
            ele.addEventListener(type, callback);
        })
        return this;  // 返回原本的
    }

    // off(type,callback)   清除绑定事件(对应回调函数清除)
    // type => 绑定的事件类型
    // callback => 事件触发时执行的函数 (事件函数中的this指向绑定事件的元素)
    GetEle.prototype.off = function (type, callback) {
        // this -> 调用此方法的实例化对象(伪数组 -> 元素的集合)
        // for(var i=0;i<this.length;i++){
        //     var ele = this[i];
        //     ele.removeEventListener(type,callback);
        // }

        this.each(function (ele, i) {
            ele.removeEventListener(type, callback);
        })
        return this;  // 返回原本的
    }


    // 基于on方法封装 绑定指定类型的事件
    GetEle.prototype.click = function (callback) {
        // this -> 调用此方法的实例化对象(伪数组 -> 元素的集合)
        // for(var i=0;i<this.length;i++){
        //     var ele = this[i];
        //     // ele.addEventListener("click",callback);
        //     this.on("click",callback);
        // }

        var _this = this;
        this.each(function (ele, i) {
            _this.on("click", callback);
        })
        return this;  // 返回原本的
    }

    GetEle.prototype.dblclick = function (callback) {
        // this -> 调用此方法的实例化对象(伪数组 -> 元素的集合)
        // for(var i=0;i<this.length;i++){
        //     var ele = this[i];
        //     // ele.addEventListener("click",callback);
        //     this.on("dblclick",callback);
        // }
        var _this = this;
        this.each(function (ele, i) {
            _this.on("dblclick", callback);
        })
        return this;  // 返回原本的
    }

    GetEle.prototype.mousedown = function (callback) {
        // this -> 调用此方法的实例化对象(伪数组 -> 元素的集合)
        // for(var i=0;i<this.length;i++){
        //     var ele = this[i];
        //     // ele.addEventListener("click",callback);
        //     this.on("mousedown",callback);
        // }

        var _this = this;
        this.each(function (ele, i) {
            _this.on("mousedown", callback);
        })
        return this;  // 返回原本的
    }

    GetEle.prototype.each = function (callback) {
        // this -> 调用此方法的实例化对象(伪数组 -> 元素的集合)
        // 遍历集合
        for (var i = 0; i < this.length; i++) {
            var a = this[i]; // 循环的当前元素
            var b = i;   // 下标
            var c = this;  // 原数组

            if (callback && typeof callback == "function") {
                callback(a, b, c)
            }
        }
        return this;  // 返回原本的

    }

    // css()  获取和设置实例中元素的样式(实例=> 获取的元素 (伪数组))
    // css("backgroundColor","red")  有两个参数就设置   ele.style.backgroundColor = "red";
    // css("backgroundColor")  有一个参数就获取  ele.style.backgroundColor(只能获取行内) => getComputedStyle(ele)["backgroundColor"]
    GetEle.prototype.css = function (cssKey, cssVal) {
        // this -> 调用此方法的实例化对象(伪数组 -> 元素的集合)
        // debugger;
        if (arguments.length >= 2) { // 两个参数就设置
            this.each(function (ele, i) {
                ele.style[cssKey] = cssVal;
            })
            return this;  // 返回原本的
        } else if (arguments.length == 1) { // 有一个参数
            // 如果参数是字符串类型,就获取 => 默认返回集合中的第一个
            if (typeof cssKey == "string") {
                return window.getComputedStyle(this[0])[cssKey];
            } else if (typeof cssKey == "object") {  // 不准确 -> 后期在优化
                // 如果参数是对象 => 拆分键值对 =>再设置样式
                // cssKey = {height:"200px",weight:"200px"}

                for (var key in cssKey) {
                    var val = cssKey[key];
                    this.css(key, val);
                    // this.each(function(ele,i){
                    //     ele.style[key] = val;
                    // })  
                }
                return this;  // 返回原本的
            }
        }

    }

    // width()  获取和设置元素的宽度
    // width("200px")   this.css("width","200px")
    // width()          this.css("width")

    GetEle.prototype.width = function (con) {
        // this -> 调用此方法的实例化对象(伪数组 -> 元素的集合)
        if (arguments.length >= 1) {
            this.each(function (ele, i) {

                // 判断是否有单位 
                // 有单位 px|pt|%|em|rem|vw|vh|vmin|vmax|
                // 没有单位  200 默认px

                var reg = /(px|pt|%|em|rem|vw|vh|vmin|vmax)$/i;
                if (reg.test(con)) {
                    ele.style.width = con;
                } else {
                    ele.style.width = con + "px";
                }
            })
            return this;  // 返回原本的
        } else {
            return parseInt(this.css("width"))
        }
    }

    // height()  获取和设置元素的高度
    // height("200px")   this.css("height","200px")
    // height()          this.css("height")

    GetEle.prototype.height = function (con) {
        // this -> 调用此方法的实例化对象(伪数组 -> 元素的集合)
        if (arguments.length >= 1) {
            this.each(function (ele, i) {

                // 判断是否有单位 
                // 有单位 px|pt|%|em|rem|vw|vh|vmin|vmax|
                // 没有单位  200 默认px

                var reg = /(px|pt|%|em|rem|vw|vh|vmin|vmax)$/i;
                if (reg.test(con)) {
                    ele.style.height = con;
                } else {
                    ele.style.height = con + "px";
                }
            })
            return this;  // 返回原本的
        } else {
            return parseInt(this.css("height"))
        }
    }

    // attr()  获取和设置实例中元素的html属性(实例=> 获取的元素 (伪数组))
    // attr("class","one")  有两个参数就设置   ele.setAttribute("class","one");
    // attr("class")  有一个参数就获取    ele.getAttribute("class","one");

    GetEle.prototype.attr = function (key, val) {
        // this -> 调用此方法的实例化对象(伪数组 -> 元素的集合)
        // debugger;
        if (arguments.length >= 2) { // 两个参数就设置
            this.each(function (ele, i) {
                ele.setAttribute(key, val);
            })
            return this;  // 返回原本的
        } else if (arguments.length == 1) { // 有一个参数
            // 如果参数是字符串类型,就获取 => 默认返回集合中的第一个
            return this[0].setAttribute(key);
        }
    }


    // prop()  获取和设置实例中元素节点的属性(property)(实例=> 获取的元素 (伪数组))
    // prop("className","one")  有两个参数就设置  ele.className = "one"
    // prop("className")  有一个参数就获取    ele.className

    GetEle.prototype.prop = function (key, val) {
        // this -> 调用此方法的实例化对象(伪数组 -> 元素的集合)
        // debugger;
        if (arguments.length >= 2) { // 两个参数就设置
            this.each(function (ele, i) {
                ele[key] = val;
            })
            return this;  // 返回原本的
        } else if (arguments.length == 1) { // 有一个参数
            // 如果参数是字符串类型,就获取 => 默认返回集合中的第一个
            return this[0][key];
        }
    }

    // addClass()  向实例中元素节点的添加一个或多个class名(多个class名用空格分隔)
    // addClass("one") 
    // addClass("one two three") 
    GetEle.prototype.addClass = function (classStr) {
        // this -> 调用此方法的实例化对象(伪数组 -> 元素的集合)
        if (arguments.length >= 1) {
            var classList = classStr.split(" ");  // ["one","two","three"]

            this.each(function (ele) {
                ele.classList.add(...classList);
            })

        }

        return this;  // 返回原本的
    }

    GetEle.prototype.removeClass = function (classStr) {
        // this -> 调用此方法的实例化对象(伪数组 -> 元素的集合)
        if (arguments.length >= 1) {
            var classList = classStr.split(" ");  // ["one","two","three"]

            this.each(function (ele) {
                ele.classList.remove(...classList);
            })
        }
        return this;  // 返回原本的
    }

    //hasClass  判断实例中元素节点的是否存在某个class名   => 如果有多个元素 只有有一个包含给定class名即可
    GetEle.prototype.hasClass = function (className) {
        // this -> 调用此方法的实例化对象(伪数组 -> 元素的集合)

        var flag = false; // 假设没有
        this.each(function (ele) {
            var res = ele.classList.contains(className);
            if (res) {
                flag = true;
            }
        })

        return flag;
    }

    // eq(index)   返回对应下标的元素形成的实例化对象
    // $(".list li").eq(0).css({background:"red"})
    GetEle.prototype.eq = function (index) {
        // this -> 调用此方法的实例化对象(伪数组 -> 元素的集合)

        var ele = this[index]; // 对应下标的元素  (原生dom元素)

        return $(ele); // 接收一个原生dom元素转化为实例化对象
    }

    // siblings() 除了被选中元素以外的其他同级元素形成的实例化对象
    // $(".list li").eq(0).siblings().css({background:"blue"}) 

    GetEle.prototype.siblings = function (index) {
        // this -> 调用此方法的实例化对象(伪数组 -> 元素的集合)

        var ele = this[0]; // 被选中的元素的第一个  

        // 被选中元素以外的其他同级元素
        // 1. 找父元素
        // 2. 通过父元素找子元素 (包含自己)
        // 3. 排除自己
        var parentEle = ele.parentElement;  // 找父元素
        var children = parentEle.children;  // 通过父元素找子元素 

        var list = Array.from(children).filter(item => item != ele); // 排除自己

        return $(list);
    }

    // 链式操作  => 方法调用完毕,返回的还是同种类型的数据 (原本/新的)

    // 数组的链式
    // var arr = [];
    // arr.concat(1,2).concat(3,4).concat(5,6);  // [1,2,3,4,5,6]

    // 字符串的链式操作
    // "YYYY-MM-DD".replace("YYYY","2023").replace("MM","04").replace("DD","13")

    // Map Set()
    // var s = new Set()
    // s.add(1).add(2).add(3)

    // jquery的链式操作
    // $(".list li").eq(0).siblings().css({background:"blue"}) 

    // $(".list li").eq(0).html("第一个").width(100).height(100).css({ background: "red" }).addClass("cur").siblings().html("其他的").width(200).height(200).css({ background: "blue" }).addClass("sib");



    // 封装$ 函数 可以根据传入的css选择器快速得到new GetEle() 实例
    function $(selector) {
        return new GetEle(selector);
    }



    $(".setHTML").click(function () {
        $(".list li").html("123123");
    })

    $(".getHTML").click(function () {
        alert($(".list li").html());
    })

    $(".hide").click(function () {
        $(".list li").hide();
    })

    $(".show").click(function () {
        $(".list li").show();
    })

    $(".setCss").click(function () {
        // 一个一个设置 => 如果样式比较多 =>写起来麻烦
        // $(".list li").css("background","red");
        // $(".list li").css("width","200px");
        // $(".list li").css("height","200px");
        // $(".list li").css("textAlign","center");
        // $(".list li").css("lineHeight","200px");

        // 如果同时设置多个样式的话 可以先整合为对象 => 再传入一整个对象(此时传入的是一个参数)
        $(".list li").css({
            backgroundColor: "red",
            width: "200px",
            height: "200px",
            textAlign: "center",
            lineHeight: "200px",
        });
    })

    $(".getCss").click(function () {
        console.log($(".list li").css("backgroundColor"));
        console.log($(".list li").css("width"));
    })

    $(".setWidth").click(function () {
        // $(".list li").width("200px");
        $(".list li").width(200);
    })

    $(".getWidth").click(function () {
        alert($(".list li").width());;
    })
    $(".setAttr").click(function () {
        $(".list li").attr("id", "demo");
        $(".list li").attr("class", "one");
        $(".list li").attr("title", "hello");
        $(".list li").attr("data-index", "0");

    })


    $(".setProp").click(function () {
        // ele.id  ele.className  ele.title   
        $(".list li").prop("id", "Box");
        $(".list li").prop("className", "one");
        $(".list li").prop("title", "world");
    })

    $(".addClass").click(function () {
        $(".list li").addClass("one two three")
    })

    $(".removeClass").click(function () {
        $(".list li").removeClass("one three")
    })

    $(".hasClass").click(function () {
        alert($(".list li").hasClass("two"))
    })

    $(".getEles").click(function () {
        console.log($(".list li"));

        var first = document.getElementById("first");
        console.log($(first));

        var liList = document.querySelectorAll(".list li");
        console.log($(liList));
    })

    $(".eq").click(function () {
        console.log($(".list li"));
        console.log($(".list li").eq(0));
        $(".list li").eq(0).css({ background: "red" })
    })

    $(".siblings").click(function () {
        console.log($(".list li"));
        console.log($(".list li").eq(0));
        console.log($(".list li").eq(0).siblings());
        $(".list li").eq(0).siblings().css({ background: "blue" })
    })

    $(".chain").click(function(){
        $(".list li").eq(0).html("第一个").width(100).height(100).css({ background: "red" }).addClass("cur").siblings().html("其他的").width(200).height(200).css({ background: "blue" }).addClass("sib");
    })

    // 原生dom元素只能使用原生的属性和方法  不能使用GetEle.prototype上的方法
    // 如果想让元素dom元素和元素的集合可以使用 GetEle.prototype上的方法  先转化为实例化对象(伪数组)

    // $(".list li").each(function(ele,i){
    //     // 如何给对应的元素设置自定义属性 data-index  = > i
    //     // ele.dataset.index = i;

    //     // 将单个元素dom转化为 实例化对象(伪数组) => 可以使用GetEle.prototype上的方法
    //     $(ele).attr("data-index",i);

     // });

    //  导航栏切换
    // $(".list li").click(function(){
    //     // $(".list li").removeClass("active");
    //     // this.classList.add("active"); // 原生dom元素只能使用原生的属性和方法
    //     // $(this).addClass("active");

    //     $(this).addClass("active");
    //     $(this).siblings().removeClass("active");

    // })



</script>

</html>